/*
 * DialogToplist
 * Version: 0.3
 * Date: 2009-02-17
 * Author: dnk07u
 */

package keyboardhero;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import keyboardhero.Util.*;

/**
 * This class provides a dialog for the help of the targets. This dialog is the in-game displaying
 * area of the toplists. This class is used to display both the local and online toplists.
 * 
 * @author {@value keyboardhero.KeyboardHero#APP_AUTHORS}
 * @version 0.2 2009-02-17
 */
final class DialogToplist extends AbstractDialog {
	
	//-------------------- VARIABLES --------------------
	
	//××××× SETTINGS VARIABLES ×××××
	
	/** The color of the newly created entry that appears in the local toplist. */
	private static final Color NEW_ENTRY_COLOR = Graphs.getColor("toplistNewEntry");
	
	private static final Font FONT_PLAIN = new Font(Font.SANS_SERIF, Font.PLAIN, 12);
	private static final Font FONT_ITALIC_BOLD = new Font(Font.SANS_SERIF, Font.ITALIC | Font.BOLD, 12);
	private static final Font FONT_LARGE_ITALIC_BOLD = new Font(Font.SANS_SERIF, Font.ITALIC | Font.BOLD, 13);
	private static final Font FONT_LARGE_BOLD = new Font(Font.SANS_SERIF, Font.BOLD, 13);
	
	//××××× CORE VARIABLES ×××××
	
	/**
	 * A JDialog is a serializable object, thus it requires a unique Serial Version ID.
	 * 
	 * @see java.io.Serializable
	 */
	private static final long serialVersionUID = 756975553676212778L;
	
	private static DialogToplist instance = null; // only used for debugging
	
	/** The main panel which provides the drawing area. */
	private JPanel mainPanel;
	
	//-------------------- CORE FUNCTIONS --------------------
	
	/**
	 * Solo constructor. The constructor creates a new Toplist object and applies it to the given
	 * frame. It is used only at the creation of the solo object.
	 * 
	 * @param frame
	 *            the owner of the dialog. This is the frame from which the dialog is displayed.
	 * @see JDialog#JDialog(Frame, boolean)
	 */
	DialogToplist(JFrame frame) {
		super(frame, null, "toplist", true);
		
		Container contentPane = getContentPane();
		mainPanel = new JPanel();
		contentPane.add("Center", mainPanel);
		mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
		
		JButton button;
		JPanel panel;
		contentPane.add("South", panel = new JPanel());
		panel.setLayout(new FlowLayout());
		panel.add(button = new JButton(Util.getMsgMnemonic("OK")));
		button.setMnemonic(Util.getLastMnemonic());
		button.setDisplayedMnemonicIndex(Util.getLastMnemonicIndex());
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				close();
			} // end method
		} // end class
		);
		
		instance = this;
	} // end constructor
	
	@Override
	protected void setPosition() {
		setLocation(Util.getPropInt(configKey + "CooX"), Util.getPropInt(configKey + "CooY"));
		setMinimumSize(new Dimension(300, 150));
	}
	
	//-------------------- EVENT FUNCTIONS --------------------
	
	/**
	 * Makes the dialog visible. This method creates an appropriate grid according to the given
	 * data. This method also checks if there is no data passed as well, thus the toplist is empty.
	 * 
	 * @param title
	 *            the title of the toplist. It is possible Local or Online Toplist, but it is
	 *            language specific.
	 * @param names
	 *            the names should appear in the toplist.
	 * @param scores
	 *            the scores assigned to the names.
	 * @param songs 
	 * 			  the author and name of the played songs.
	 * @param factors
	 *            the speed in which the game was played.
	 * @param lineCount
	 *            the number of lines passed to this method.
	 * @param newEntryNum
	 *            the newly created entry that will be highlighted by the
	 *            {@link DialogToplist#NEW_ENTRY_COLOR}. -1 indicates there isn't any.
	 */
	void open(String title, String[] names, Integer[] scores, String[] songs, Float[] factors, int lineCount, int newEntryNum) {
		if(lineCount > names.length) lineCount = names.length;
		
		setContent(names, scores, songs, factors, lineCount, newEntryNum);
		
		setTitle(title);
		//setLocationRelativeTo(instance.getOwner());
		try {
			setVisible(true);
		} catch(Throwable e) {
			dispose();
			((DialogToplist)getNewInstance((JFrame)this.getOwner())).open(title, names, scores, songs, factors, lineCount, newEntryNum);
		}
	} // end method
	
	void open(String title, String statusText, boolean isItalic) {
		setStatusText(statusText, isItalic);
		
		setTitle(title);
		//setLocationRelativeTo(instance.getOwner());
		try {
			setVisible(true);
		} catch(Throwable e) {
			dispose();
			((DialogToplist)getNewInstance((JFrame)this.getOwner())).open(title, statusText, isItalic);
		}
	}
	
	synchronized void setStatusText(String statusText, boolean isItalic) {
		mainPanel.removeAll();
		JTextArea textArea;
		mainPanel.setLayout(new BorderLayout());
		mainPanel.add(textArea = new JTextArea(statusText), BorderLayout.CENTER);
		textArea.setEditable(false);
		textArea.setBackground(new Color(mainPanel.getBackground().getRGB()));
		textArea.setHighlighter(null);
		textArea.setBorder(new EmptyBorder(10, 10, 10, 10));
		textArea.setFont(isItalic ? FONT_LARGE_ITALIC_BOLD : FONT_LARGE_BOLD);
		
		if(isItalic) {
			JProgressBar progressBar = new JProgressBar();
			progressBar.setIndeterminate(true);
			mainPanel.add(progressBar, BorderLayout.SOUTH);
		}
		pack();
	}
	
	synchronized void setContent(String[] names, Integer[] scores, String[] songs, Float[] factors, int lineCount, int newEntryNum) {
		mainPanel.removeAll();
		
		JLabel label;
		
		if(lineCount < 1) {
			mainPanel.setLayout(new BorderLayout());
			mainPanel.add(label = new JLabel(Util.getMsg("EmptyToplist"), JLabel.CENTER));
			label.setFont(FONT_LARGE_ITALIC_BOLD);
		} // end if
		else {
			mainPanel.setLayout(new DynamicGridLayout(lineCount + 2, 5, 20, 5));
			mainPanel.add(new JLabel());
			mainPanel.add(label = new JLabel(Util.getMsg("Name"), JLabel.LEFT));
			label.setFont(FONT_LARGE_BOLD);
			mainPanel.add(label = new JLabel(Util.getMsg("Score"), JLabel.CENTER));
			label.setFont(FONT_LARGE_BOLD);
			mainPanel.add(label = new JLabel(Util.getMsg("Song"), JLabel.CENTER));
			label.setFont(FONT_LARGE_BOLD);
			mainPanel.add(label = new JLabel(Util.getMsg("Speed"), JLabel.RIGHT));
			label.setFont(FONT_LARGE_BOLD);
			
			mainPanel.add(new JLabel());
			mainPanel.add(new JLabel());
			mainPanel.add(new JLabel());
			mainPanel.add(new JLabel());
			mainPanel.add(new JLabel());
			
			final String name = Util.getProp("name");
			
			mainPanel.setFont(FONT_PLAIN);
			
			for(int i = 0; i < lineCount; i++) {
				if(i == newEntryNum) {
					mainPanel.add(label = new JLabel((i + 1) + ".", JLabel.LEFT));
					label.setFont(FONT_ITALIC_BOLD);
					label.setForeground(NEW_ENTRY_COLOR);
					mainPanel.add(label = new JLabel(((names[i].length() == 0) ? Util.getMsg("Anonymous") : names[i]), JLabel.LEFT));
					label.setFont(FONT_ITALIC_BOLD);
					label.setForeground(NEW_ENTRY_COLOR);
					mainPanel.add(label = new JLabel(scores[i].toString(), JLabel.CENTER));
					label.setFont(FONT_ITALIC_BOLD);
					label.setForeground(NEW_ENTRY_COLOR);
					mainPanel.add(label = new JLabel(songs[i], JLabel.CENTER));
					label.setFont(FONT_ITALIC_BOLD);
					label.setForeground(NEW_ENTRY_COLOR);
					mainPanel.add(label = new JLabel(Util.floatToSpeed(factors[i]), JLabel.RIGHT));
					label.setFont(FONT_ITALIC_BOLD);
					label.setForeground(NEW_ENTRY_COLOR);
				} // end if
				else if(names[i].length() != 0 && names[i].equals(name)) {
					mainPanel.add(label = new JLabel((i + 1) + ".", JLabel.LEFT)); //$SUP-DCCB$
					label.setFont(FONT_ITALIC_BOLD);
					mainPanel.add(label = new JLabel(names[i], JLabel.LEFT));
					label.setFont(FONT_ITALIC_BOLD);
					mainPanel.add(label = new JLabel(scores[i].toString(), JLabel.CENTER));
					label.setFont(FONT_ITALIC_BOLD);
					mainPanel.add(label = new JLabel(songs[i], JLabel.CENTER));
					label.setFont(FONT_ITALIC_BOLD);
					mainPanel.add(label = new JLabel(Util.floatToSpeed(factors[i]), JLabel.RIGHT));
					label.setFont(FONT_ITALIC_BOLD);
				} // end if
				else {
					mainPanel.add(label = new JLabel((i + 1) + ".", JLabel.LEFT)); //$SUP-DCCB$
					label.setMaximumSize(new Dimension(10, 900));
					mainPanel.add(new JLabel(((names[i].length() == 0) ? Util.getMsg("Anonymous") : names[i]), JLabel.LEFT));
					mainPanel.add(new JLabel(scores[i].toString(), JLabel.CENTER));
					mainPanel.add(new JLabel(songs[i], JLabel.CENTER));
					mainPanel.add(new JLabel(Util.floatToSpeed(factors[i]), JLabel.RIGHT));
				} // end else
			} // end for
		} // end else: linecount>=1
		
		pack();
	}
	
	//-------------------- OVERRIDING FUNCTIONS --------------------
	
	@Override
	protected void closure() {
		if(wasOpened) {
			Util.setProp(configKey + "CooX", getX());
			Util.setProp(configKey + "CooY", getY());
		}
	}
	
	@Override
	protected AbstractDialog getNewInstance(JFrame frame) {
		return new DialogToplist(frame);
	}
	
	//-------------------- DEBUGGING FUNCTIONS --------------------
	
	/**
	 * Creates a string containing the most important information about the
	 * {@link DialogToplist#instance main DialogToplist} object. This method is used only for
	 * debugging and testing purposes.
	 * 
	 * @return the created string.
	 */
	static String getString() {
		return "DialogToplist(visible=" + instance.isVisible() + "; location=" + instance.getLocationOnScreen() + "; bounds=" + instance.getBounds() + ")";
	} // end method
	
	/**
	 * This method serves security purposes. Provides an integrity string that will be checked by
	 * the {@link Connection#integrityCheck()} method; thus the application can only be altered if
	 * the source is known. Every class in the {@link keyboardhero} package has an integrity string.
	 * 
	 * @return the string of this class used for integrity checking.
	 */
	static String getIntegrityString() {
		return "u%c`J˝o!dfdU_3345TasT";
	} // end method
	
	/**
	 * The tester object of this class. It provides a debugging menu and unit tests for this class.
	 * Its only purpose is debugging or testing.
	 */
	final static Tester TESTER = new Tester("DialogToplist", new String[] {"getString()", "open(title, names, scores, speeds, lineCount, newEntryNum)", "close()"} /* end initialization */) { //$SUP-PPA$
		void menu(int choice) throws Exception {
			switch(choice) {
				case 5:
					System.out.println(getString());
					break;
				case 6:
					final String title = readString("String title");
					final String[] names = readStrings("String[] names");
					int[] ints = readInts("Integer[] scores");
					Integer[] integers = new Integer[ints.length];
					for(int i = 0; i < ints.length; i++) {
						integers[i] = ints[i];
					} // end for
					final Integer[] scores = integers;
					final String[] songs = readStrings("String[] songs");
					float[] floats = readFloats("Float[] factors");
					Float[] floatobjs = new Float[floats.length];
					for(int i = 0; i < floats.length; i++) {
						floatobjs[i] = floats[i];
					} // end for
					final Float[] factors = floatobjs;
					ints = readInts("int lineCount | int newEntryNum");
					final int[] ints2 = ints;
					(new Thread() {
						public void run() {
							instance.open(title, names, scores, songs, factors, ints2[0], ints2[1]);
						} // end method
					}).start(); // end class
					break;
				case 7:
					instance.close();
					break;
				default:
					baseMenu(choice);
					break;
			} // end switch
		} // end method
		
		void runUnitTests() throws Exception {
			higherTestStart("Toplist");
			
			testEq("getIntegrityString()", "u%c`J˝o!dfdU_3345TasT", DialogToplist.getIntegrityString());
			
			testStart("open");
			instance.setVisible(false);
			(new Thread() {
				public void run() {
					instance.open("title", new String[] {"name1", "name2", "name3"} /* end constructor */, new Integer[] {50, 100, 150} /* end constructor */, new String[] {"Moonlight", "Joplin: The Entertainer", "Mozart: Symphony #40"} /* end constructor */, new Float[] {0.5f, 1f, 1.5f} /* end constructor */, 3, 1);
				} // end method
			}).start(); // end class
			Thread.sleep(500);
			testEnd(instance.isVisible());
			
			testStart("rows");
			final GridLayout gridlayout = (GridLayout)instance.mainPanel.getLayout();
			final int rows = gridlayout.getRows();
			testEnd(rows == 5, "", "Num of rows should be 5: " + rows);
			
			int columns = gridlayout.getColumns();
			test("columns", columns == 5, "", "Num of columns should be 5: " + columns);
			
			testStart("close");
			(new Thread() {
				public void run() {
					instance.setVisible(true);
				} // end method
			}).start(); // end class
			Thread.sleep(100);
			instance.close();
			testEnd(!instance.isVisible());
			
			higherTestEnd();
		} // end method
	}; // end class
	
	/**
	 * Starts the class's developing menu. If this build is a developer's one it starts the
	 * application in a normal way with the exception that it starts the debugging tool for this
	 * class as well; otherwise exits with an error message.
	 * 
	 * @param args
	 *            the arguments given to the program.
	 * @see KeyboardHero#startApp()
	 */
	public static void main(String[] args) {
		Tester.mainer(args, TESTER);
	} // end method
} // end class
